
package sicnu.cs.aps.common.util;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.SignatureException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import sicnu.cs.aps.config.AppProperties;
import sicnu.cs.aps.domain.entity.Role;
import sicnu.cs.aps.domain.entity.User;
import sicnu.cs.aps.domain.vo.Auth;

import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletResponse;
import java.security.Key;
import java.util.*;
import java.util.stream.Collectors;

@Component
public class JwtUtil {
    /**
     * 用于签名 Access Token
     */
    private final Key key;
    /**
     * 用于签名 Refresh Token
     */
    private final Key refreshKey;
    private final AppProperties appProperties;

    public JwtUtil(AppProperties appProperties) {
        this.appProperties = appProperties;
        key = new SecretKeySpec(Base64.getDecoder().decode(appProperties.getJwt().getKey()), "HmacSHA512");
        refreshKey = new SecretKeySpec(Base64.getDecoder().decode(appProperties.getJwt().getRefreshKey()), "HmacSHA512");
    }

    /**
     * 生成一个Token
     *
     * @param userDetails  payLoad负载信息
     * @param timeToExpire Token的有效期
     */
    public String createJWTToken(UserDetails userDetails, long timeToExpire) {
        return createJWTToken(userDetails, timeToExpire, key);
    }

    /**
     * 根据用户信息生成一个 JWT
     *
     * @param userDetails  用户信息
     * @param timeToExpire 毫秒单位的失效时间
     * @param signKey      签名使用的 key
     * @return JWT
     */
    public String createJWTToken(UserDetails userDetails, long timeToExpire, Key signKey) {
        User user = (User) userDetails;
        var authorities = user.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toList());
        return Jwts
                .builder()
                .setId("sicnu")
                .setSubject(String.valueOf(((User) userDetails).getId()))
                .claim("authorities", authorities)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + timeToExpire))
                .signWith(signKey, SignatureAlgorithm.HS512).compact();
    }

    public String createAccessToken(UserDetails userDetails) {
        return createJWTToken(userDetails, appProperties.getJwt().getAccessTokenExpireTime());
    }

    public String createRefreshToken(UserDetails userDetails) {
        return createJWTToken(userDetails, appProperties.getJwt().getRefreshTokenExpireTime(), refreshKey);
    }

    public boolean validateAccessToken(String jwtToken) {
        return validateToken(jwtToken, key);
    }

    public boolean validateRefreshToken(String jwtToken) {
        return validateToken(jwtToken, refreshKey);
    }

    /**
     * 返回Token是否有效
     */
    public boolean validateToken(String jwtToken, Key signKey) {
        return parseClaims(jwtToken, signKey).isPresent();
    }

    public Auth buildAccessTokenWithRefreshToken(String jwtToken) {
        String accessToken = parseClaims(jwtToken, refreshKey)
                .map(claims -> Jwts.builder()
                        .setClaims(claims)
                        .setExpiration(new Date(System.currentTimeMillis() + appProperties.getJwt().getAccessTokenExpireTime()))
                        .signWith(key, SignatureAlgorithm.HS512).compact())
                .orElseThrow();
        return Auth.builder()
                .token(accessToken)
                .refreshToken(jwtToken)
                .tokenExpireTime(System.currentTimeMillis() + appProperties.getJwt().getAccessTokenExpireTime())
                .build();
    }

    /**
     * 解析验证Token
     *
     * @param jwtToken 接收的Token
     * @param signKey  签名
     * @return 保存的信息（claims）
     */
    public Optional<Claims> parseClaims(String jwtToken, Key signKey) {
        return Optional.ofNullable(Jwts.parserBuilder().setSigningKey(signKey).build().parseClaimsJws(jwtToken).getBody());
    }

    /**
     * 不检查是否过期（验证Token是否有效）
     */
    public boolean validateWithoutExpiration(String jwtToken) {
        try {
            Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(jwtToken);
            return true;
        } catch (ExpiredJwtException | SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException e) {
            if (e instanceof ExpiredJwtException) {
                return true;
            }
        }
        return false;
    }

    public Key getKey() {
        return key;
    }

    public Key getRefreshKey() {
        return refreshKey;
    }

    /**
     * 设置Token的Cookie用于返回给前端
     */
    public Auth setCookiesForTokens(HttpServletResponse res, UserDetails user) {
        var accessToken = createAccessToken(user);
        var refreshToken = createRefreshToken(user);
        return Auth.builder()
                .token(accessToken)
                .refreshToken(refreshToken)
                .tokenExpireTime(System.currentTimeMillis() + appProperties.getJwt().getAccessTokenExpireTime())
                .build();
    }
}
